import { ACEvents, AuthClientOption, AuthMethod, CacheType, createDefaultOption, CustomLoginOption, PhoneLoginOption, QrCodeOption, ResponseBody, RouterMode, TokenInfo } from "model/authClient.model";
import * as EventEmitter from 'events';
import { delCookie, delSession, getCookie, getSession, setCookie, setSession } from "utils";
import { httpClient } from './utils/httpClient'
import httpClientText from './utils/httpClientText'
import LogCollect from './logCollect'
// @ts-ignore
import * as queryString from 'query-string'
export const BaseName = 'JH_AUTH_CLIENT_BASE'
declare var window: any;
const getUrlParam = (paramName, routerMode: RouterMode, url?: string): string => {
	const _location = url ? new URL(url) : window.location;
	var reg = new RegExp("(^|&)" + paramName + "=([^&]*)(&|$)");    //构造一个含有目标参数的正则表达式对象
	if (routerMode === RouterMode.HASH) {
		const hash = _location.hash;
		const str = hash.split('?')[1]
		const parsed = queryString.parse(str);
		return parsed[paramName] as string
	} else {
		var r = _location.search.substr(1).match(reg);            //匹配目标参数
		if (r != null) return decodeURI(r[2]); return null;             //返回参数值
	}
}
const urlDelParams = (name, routerMode: RouterMode) => {
	if (routerMode === RouterMode.HISTORY) {
		const parsed = queryString.parse(location.search);
		delete parsed[name]
		const loca = window.location
		const newurl = Object.keys(parsed).length > 0 ? loca.origin + loca.pathname + queryString.stringify(parsed) : loca.origin + loca.pathname
		// 如果支持的话 修改路径
		if ('pushState' in history) {
			history.replaceState(null, null, newurl)
		} else {
			window.location.replace(newurl)
		}
	} else {
		const hash = window.location.hash
		let [pre, suf] = hash.split('?')
		const parsed = queryString.parse(suf);
		delete parsed[name];
		if (Object.keys(parsed).length > 0) {
			let newhash = queryString.stringify(parsed)
			window.location.hash = pre + '?' + newhash
		} else {
			window.location.hash = pre
		}
	}
}
const filterUrlParams = (name, routerMode: RouterMode) => {
	if (routerMode === RouterMode.HISTORY) {
		const parsed = queryString.parse(location.search);
		delete parsed[name]
		const loca = window.location
		const newurl = Object.keys(parsed).length > 0 ? loca.origin + loca.pathname + queryString.stringify(parsed) : loca.origin + loca.pathname
		return newurl
	} else {
		const hash = window.location.hash
		let [pre, suf] = hash.split('?')
		const parsed = queryString.parse(suf);
		delete parsed[name];
		if (Object.keys(parsed).length > 0) {
			let newhash = queryString.stringify(parsed)
			return window.location.origin + window.location.pathname + pre + '?' + newhash
		} else {
			return window.location.origin + window.location.pathname + pre
		}
	}
}
class AuthClient {
	private _tokenInfo: TokenInfo
	private _user: Object
	private _emitter = new EventEmitter()
	private _href: string
	private _permissionList: any[]
	private _dingQrCodeEleId: string;           // 钉钉登录二维码容器id
	public logRecordEvent: void                 // 日志事件
	public logRecordPage: void                  // 日志跳转
	public logDestroy: any                      // 日志销毁方法
	get token() {
		return this._tokenInfo.access_token
	}
	set tokenInfo(tokenInfo) {
		this._tokenInfo = tokenInfo
	}
	get tokenInfo() {
		return this._tokenInfo
	}
	get user() {
		return this._user
	}
	set user(user) {
		this._user = user
		if (user) {
			this.logInit(this.option.resourceCode, user) // 日志收集初始化
		}
	}
	get permissionList() {
		return this._permissionList
	}
	set permissionList(permissionList) {
		this._permissionList = permissionList
	}
	constructor(public option: AuthClientOption) {
		this._dingQrCodeEleId = null;
		if (!option.SSOServer) {
			setTimeout(() => {
				this._emitter.emit(ACEvents.ERROR, 'SSOServer必传')
			}, 0)
		} else {
			this.option = createDefaultOption(option)
			this._href = filterUrlParams('tmpTicket', option.routerMode)
			const isDirty = getSession(BaseName + '_isDirty')
			this.init(isDirty)
		}
	}
	private init(isDirty) {
		if (!!isDirty && this.getDateByCache()) {
			const { user, tokenInfo, permissionList } = this;
			const tmpTicket = getUrlParam("tmpTicket", this.option.routerMode);
			if (!!tmpTicket && tmpTicket !== 'null' && tmpTicket !== 'undefined') {
				this.validateBytmpTicket(tmpTicket)
			} else {
				setTimeout(() => {
					this._emitter.emit(ACEvents.INITED, { umsAdmin: user, tokenInfo, permissionList, loginAction: false })
				}, 0)
			}
		} else {
			// 缓存过期或者不缓存的情况
			switch (this.option.method) {
				case AuthMethod.CUSTOM:
					this.connect()
					return;
				case AuthMethod.SSO:
					this.initSSOMethod()
					return;
			}
		}
	}
	/**
	 * connect
	 * @return  嗅探客户端是否登录
	 * @description
	 * @author  jsyang
	 * @date  2022-03-09 14:57:03
	 */
	private connect() {
		const tmpTicket = getUrlParam("tmpTicket", this.option.routerMode);
		if (tmpTicket) {
			// 如果页面存在 tmpTicket
			setSession(BaseName + '_isDirty', true)
			return this.validateBytmpTicket(tmpTicket, true)
		} else {
			const params = {
				code: this.option.resourceCode,
				returnUrl: encodeURIComponent(location.href),
				t: new Date().getTime(),
			}
			httpClientText(this.option.SSOServer + '/web/login', params, 'GET').then((res: string) => {
				if (res) {
					const tmpTicket = getUrlParam("tmpTicket", this.option.routerMode, res);
					if (tmpTicket) {
						setSession(BaseName + '_isDirty', true)
						return this.validateBytmpTicket(tmpTicket, true)
					}
				} else {
					this._emitter.emit(ACEvents.INITED, false)
				}
			})
		}
	}
	private getDateByCache() {
		const cacheMode = this.option.cacheMode
		switch (cacheMode) {
			case CacheType.COOKIE:
				return this.getCookieCache()
			case CacheType.SESSION:
				return this.getSessionCache()
			default:
				return false
		}
	}
	private getSessionCache() {
		// 从前端session缓存中获取数据信息
		this.user = getSession(BaseName + '_user')
		this.tokenInfo = getSession(BaseName + '_tokenInfo')
		this.permissionList = getSession(BaseName + '_permissionList')
		if (this.user && this.tokenInfo) {
			return true
		} else {
			this.removeAllCache()
			return false
		}
	}
	private getCookieCache() {
		// 从前端cookie缓存中获取数据信息
		this.user = getCookie(BaseName + '_user', true)
		this.tokenInfo = getCookie(BaseName + '_tokenInfo', true)
		this.permissionList = getCookie(BaseName + '_permissionList', true)
		if (this.user && this.tokenInfo) {
			return true
		} else {
			this.removeAllCache()
			return false
		}
	}
	private removeAllCache() {
		setSession(BaseName + '_isDirty', false)
		delCookie(BaseName + '_user')
		delCookie(BaseName + '_tokenInfo')
		delCookie(BaseName + '_permissionList')
		delSession(BaseName + '_user')
		delSession(BaseName + '_tokenInfo')
		delSession(BaseName + '_permissionList')
		delSession(BaseName + '_phoneSms')
	}
	private setAllCookies() {
		const sec = (this.tokenInfo && this.tokenInfo.expires_in) || 3600
		const opt = {
			expires: sec / 3600 / 24
		}
		setCookie(BaseName + '_user', this.user, opt)
		setCookie(BaseName + '_tokenInfo', this.tokenInfo, opt)
		setCookie(BaseName + '_permissionList', this.permissionList, opt)
	}
	private setAllSession() {
		setSession(BaseName + '_user', this.user)
		setSession(BaseName + '_tokenInfo', this.tokenInfo)
		setSession(BaseName + '_permissionList', this.permissionList)
	}
	private setDataCache() {
		const cacheMode = this.option.cacheMode
		switch (cacheMode) {
			case CacheType.COOKIE:
				return this.setAllCookies()
			case CacheType.SESSION:
				return this.setAllSession()
			default:
				return false
		}
	}
	private initSSOMethod() {
		const tmpTicket = getUrlParam("tmpTicket", this.option.routerMode);
		setSession(BaseName + '_isDirty', true)
		if (!!tmpTicket && tmpTicket !== 'null' && tmpTicket !== 'undefined') {
			this.validateBytmpTicket(tmpTicket, true, false)
		} else {
			// 如果没有tmpTicket临时票据，跳转至sso登录界面
			const url = encodeURIComponent(this._href)
			if (this.option.DingAutoLogin) {
				window.location.href = `${this.option.SSOServer}/ddLoad?code=${this.option.resourceCode}&returnUrl=${url}`;
			} else {
				window.location.href = `${this.option.SSOServer}?code=${this.option.resourceCode}&returnUrl=${url}`;
			}
			//returnUrl为非必填项，如果不填则默认跳转第三方业务系统在4A注册时填写的首页地址，如果填写则跳转填写地址
		}
	}
	private async logInit(resourceCode, umsAdmin) {
		let option = this.option
		let logServerUrl = option.logCollect?.logServer
		if (option.logCollect) {
			if (!logServerUrl) {
				this._emitter.emit(ACEvents.LOGERROR, '请输入日志收集地址')
				return
			}

			let logInstance = new LogCollect({
				resourceCode: resourceCode,
				logServer: logServerUrl,
				interval: option.logCollect.interval,
				production: option.logCollect.production,
			})
			logInstance.on('error', (e) => {
				this._emitter.emit(ACEvents.LOGERROR, e)
			})
			await logInstance.setUser(umsAdmin)
			this.logRecordEvent = logInstance.recordEvent.bind(logInstance)    // 记录事件
			this.logRecordPage = logInstance.recordPage.bind(logInstance)      // 记录页面
			this.logDestroy = logInstance.destroy.bind(logInstance)            // 销毁
		}
	}
	/**
	 * 票据验证接口
	 * @param tmpTicket { String }       票据
	 * @param emitEvent { Boolean }      是否触发事件
	 * @param loginAction { Boolean }    是否账号密码登录
	 */
	private validateBytmpTicket(tmpTicket: string, emitEvent: boolean = true, loginAction: boolean = true) {
		return new Promise((reslove, reject) => {
			httpClient(this.option.SSOServer + '/web/verify-tmp-ticket?tmpTicket=' + tmpTicket, {}, 'post')
				.then(res => {
					if (res.code === 0) {
						res.data.loginAction = loginAction
						const { umsAdmin, tokenInfo, permissionList } = res.data;
						this.user = umsAdmin;
						this.tokenInfo = tokenInfo;
						this.permissionList = permissionList || []
						this.setDataCache()
						if (emitEvent) {
							this._emitter.emit(ACEvents.INITED, { ...res.data })
						}
						setTimeout(() => {
							this.fixUrlQuery()
						}, 0)
						reslove(res.data)
					} else {
						if (emitEvent) {
							this._emitter.emit(ACEvents.ERROR, res)
						}
						reject(res)
					}
				}).catch(err => {
				if (emitEvent) {
					this._emitter.emit(ACEvents.ERROR, err)
				}
				reject(err)
			})
		})
	}
	public serverLogout() {
		return new Promise((reslove, reject) => {
			const { loginName: username } = this.user as any
			const { access_token: authToken } = this.tokenInfo
			httpClient(this.option.SSOServer + '/web/users/login-out', { username, authToken }, 'GET')
				.then(res => {
					if (res) {
						reslove(res)
					} else {
						reject(res)
					}
				}).catch(err => {
				reject(err)
			})
		})
	}
	private fixUrlQuery() {
		urlDelParams('tmpTicket', this.option.routerMode)
	}
	/**
	 * upDataSubmit
	 * @param  参数 endPath
	 * @param  参数 params
	 * @return
	 * @description
	 * @author  jsyang
	 * @date  2022-03-09 17:05:31
	 */
	private upDataSubmit(url, params) {
		return httpClientText(this.option.SSOServer + url + '?' + queryString.stringify(params), {}, 'POST').then((res: any) => {
			if (typeof res === 'string') {
				const tmpTicket = getUrlParam("tmpTicket", this.option.routerMode, res);
				if (tmpTicket) {
					setSession(BaseName + '_isDirty', true)
					return this.validateBytmpTicket(tmpTicket, false)
				}
			} else {
				const obj = { ...res }
				if (res.msg.includes('|') && res.msg.includes('密码')) {
					const arr = res.msg.split('|')
					obj.data = parseInt(arr[1])
					obj.msg = arr[0]
				}
				return Promise.reject(obj)
			}
		})
	}
	on(event, listener) {
		this._emitter.addListener(event, listener);
	}
	off(event, listener) {
		this._emitter.removeListener(event, listener);
	}
	// 获取页面ID
	private _$ = (id)=>{
		return document.getElementById(id)
	}
	/**
	 * 国密认证
	 * */
	isTwoCheck(username) {
		// 是否需要动态认证
		return httpClientText(this.option.SSOServer + "/web/app-password-enable", {
			resourceCode: this.option.resourceCode,
			username:username,
		}, "GET").then(res => {
			return res
		})
	}
	twoCodeCheck(password, params) {
		// 动态验证码是否正确
		const data = { username: params.username || params.phone, password: password, isDing:0 }
		const url = this.option.SSOServer + "/web/app-password-check?" + queryString.stringify(data)
		return httpClientText(url, {}, "GET").then((res: any) => {
			if (res.code === 0) {
				return this.upDataSubmit("/web/do-login", params)
			} else {
				return res
			}
		})
	}
	twoCodeModal(params) {
		return new Promise((reslove, reject) => {
			// 页面弹框
			const style={
				auth:'position: fixed;z-index: 999;top: 0;bottom: 0;right: 0;left: 0;background: rgba(0,0,0,.5);display: flex;align-items: center;justify-content: center;',
				main:'width: 520px;height: 230px;background: #fff;',
				head:'width: 100%;display: flex;box-sizing: border-box;justify-content: space-between;padding: 16px 24px;color: rgba(0, 0, 0, 0.85);border-bottom: 1px solid #f0f0f0;',
				close:'cursor: pointer;color: #333;',
				body:'box-sizing: border-box;width: 100%;padding: 16px 24px 30px;position: relative;',
				input:'box-sizing: border-box;margin: 0;font-variant: tabular-nums;list-style: none;display: inline-block;width: 100%;padding: 4px 11px;color: rgba(0, 0, 0, 0.85);font-size: 14px;line-height: 36px;background-color: #fff;background-image: none;border: 1px solid #d9d9d9;border-radius: 2px;transition: all 0.3s;outline:none;',
				open:'display: block;padding-top: 3px;font-size: 15px;color: #40a9ff;text-decoration: underline;cursor: pointer;opacity: 0.9;',
				err:'display: none;position: absolute;bottom: 7px;color: red;',
				foot:'box-sizing: border-box;width: 100%;padding: 16px 24px;border-top: 1px solid #f0f0f0;',
				button:'float: right;line-height: 32px;font-weight: 400;white-space: nowrap;text-align: center;background-image: none;border: 1px solid transparent;cursor: pointer;transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);user-select: none;height: 32px;padding: 0 15px;font-size: 14px;border-radius: 2px;color: rgba(0, 0, 0, 0.85);color: #fff;background: #1890ff;border-color: #1890ff;',
			}
			const html = document.createElement("div")
			html.innerHTML = `<div id="state-modal-auth" style="${style.auth}">
								<div class="main-modal-auth" style="${style.main}">
									<div class="head-modal-auth" style="${style.head}">
										<span>动态口令</span>
										<span id="close-modal-auth" style="${style.close}">✕</span>
									</div>
									<div class="body-modal-auth" style="${style.body}">
										<input id="input-modal-auth" style="${style.input}" placeholder="请输入生成的动态口令" maxlength="9"/>
										<a id="open-modal-auth" style="${style.open}">如何获取动态口令？</a>
										<span id="err-modal-auth" style="${style.err}">验证码不能为空</span>
									</div>
									<div class="foot-modal-auth" style="${style.foot}">
										<button id="submit-modal-auth" style="${style.button}">确定</button>
									</div>
								</div>
							</div>`
			document.body.appendChild(html)
			// 弹框操作
			this._$("close-modal-auth").onclick = () => {
				this.destroyModal()
			}
			this._$("open-modal-auth").onclick = () => {
				const name = params.username || params.phone
				const herf = this.option.SSOServer + "/step?username=" + name
				window.open(herf)
			}
			const input = this._$("input-modal-auth")
			const err = this._$("err-modal-auth")
			this._$("submit-modal-auth").onclick = () => {
				const value = input.value
				err.style['display'] = "none"
				input.style['border-color'] = "#d9d9d9"
				if (value !== "") {
					this.twoCodeCheck(value, params).then((res: any) => {
						reslove(res)
					})
				} else {
					err.style['display'] = "block"
					input.style['border-color'] = "red"
				}
			}
			input.onfocus = () =>{
				input.style['box-shadow'] = "0 0 4px #40a9ff"
				input.style['border-color'] = "#40a9ff"
			}
			input.onblur=()=>{
				input.style['box-shadow'] = "0 0 0 #fff"
				input.style['border-color'] = err.style['display']==='none'?'#d9d9d9':'red'
			}
		})
	}
	public destroyModal(){
		// 销毁弹框
		const modal = this._$("state-modal-auth")
		if(modal){
			modal.parentNode.parentNode.removeChild(modal.parentNode)
		}
	}
	/* 账号密码登录 */
	login({ username, password, loginCode, loginCodeId }: CustomLoginOption) {
		if (!username) {
			return Promise.reject('请输入用户名')
		}
		if (!password) {
			return Promise.reject('请输入密码')
		}
		const params = {
			username,
			password,
			loginCode,
			loginCodeId,
			code: this.option.resourceCode,
			returnUrl: encodeURIComponent(location.href)
		}
		return this.isTwoCheck(username).then((res: any) => {
			if (res.code === 0) {
				if (res && res.data) {
					return this.twoCodeModal(params)
				} else {
					return this.upDataSubmit("/web/do-login", params)
				}
			} else {
				return res
			}
		})
	}
	public getImageCode() { // 图片验证码
		return httpClientText(this.option.SSOServer + '/web/image-code', {}, 'get', '', 'arraybuffer').then((res: any) => {
			if (res instanceof Object && res.logincodeid) {
				return {
					loginCodeId: res.logincodeid,
					img: "data:image/png;base64," + window.btoa(new Uint8Array(res.data).reduce((data, byte) => data + String.fromCharCode(byte), ""))
				}
			} else {
				return Promise.reject(res)
			}
		})
	}
	/**
	 * sendSmsCode
	 * @param  手机号
	 * @return
	 * @description
	 * @author  jsyang
	 * @date  2022-03-09 17:23:34
	 */
	public sendSmsCode(phone: string): Promise<any> { //短信验证码
		const reg = /^(((13[0-9]{1})|(15[0-9]{1})|(17[0-9]{1})|(19[0-9]{1})|(18[0-9]{1}))+\d{8})$/;
		if (!phone) {
			return Promise.reject('手机号不能为空');
		}
		if (phone.length !== 11) {
			return Promise.reject('手机格式不对');
		}
		if (!reg.test(phone)) {
			return Promise.reject('请输入有效的手机号码');
		}
		const params = { phone }
		return httpClientText(this.option.SSOServer + '/web/sms-code?' + queryString.stringify(params), {}, 'post').then((res: ResponseBody) => {
			if (res.code === 0) {
				return httpClientText(this.option.SSOServer + '/web/sms-code-time', {}, 'get').then((res: ResponseBody) => {
					if (res.code === 0) {
						return {
							code: 0,
							data: res.data, // 短信验证码限流时间
							msg: '短信发送成功',
						}
					} else {
						return Promise.reject(res)
					}
				})
			} else {
				return Promise.reject(res)
			}
		})
	}
	public loginByPhone({ smsMob, smsCode }: PhoneLoginOption) {
		if (!smsMob) {
			return Promise.reject('请输入手机号')
		}
		if (!smsCode) {
			return Promise.reject('请输入短信验证码')
		}
		const params = {
			phone: smsMob,
			mobileCode: smsCode,
			code: this.option.resourceCode,
			returnUrl: encodeURIComponent(location.href)
		}
		return this.isTwoCheck(smsMob).then((res: any) => {
			if (res.code === 0) {
				if (res && res.data) {
					return this.twoCodeModal(params)
				} else {
					return this.upDataSubmit("/web/sms-login", params)
				}
			} else {
				return res
			}
		})
	}
	/**
	 * 二维码监听公用方法
	 * @param  obj 参数
	 * @param  redirectUrl 参数
	 * */
	private qrCommonListener( ddappid, redirectUrl){
		const handleMessage = (event) => {
			const { origin } = event;
			const ddUrl = "https://login.dingtalk.com";
			const gztUrl = "https://login.gzt.drc.jiangxi.gov.cn";
			//判断是否来自钉钉或者赣政通扫码事件。
			if (origin == ddUrl || origin == gztUrl) {
				//获取到loginTmpCode后就可以在这里构造跳转链接进行跳转了
				const serverUrl = origin == ddUrl ? this.option.SSOServer + "/web/ding-login": this.option.SSOServer
				const loginTmpCode = origin == ddUrl ? event.data : event.data.code;
				const resourcesCode = this.option.resourceCode ;
				const returnUrl = window.btoa(redirectUrl || location.href);
				const ddObj = serverUrl + "&loginTmpCode=" + loginTmpCode + "&state=" + resourcesCode + "," + returnUrl;
				const gztObj = "code=" + loginTmpCode + "&state=STATE," + resourcesCode + "," + returnUrl;
				//获取到loginTmpCode后就可以在这里构造跳转链接进行跳转了
				window.location.href = origin == ddUrl
						? "https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=" + ddappid +
						"&response_type=code&scope=snsapi_login&state=STATE&redirect_uri=" + ddObj
						: serverUrl + "/web/gzt-login?" + gztObj;
			}
		};
		if (typeof window.addEventListener != 'undefined') {
			window.addEventListener('message', handleMessage, false);
		} else if (typeof window.attachEvent != 'undefined') {
			window.attachEvent('onmessage', handleMessage);
		}
	}
	// 钉钉
	async qrCode(opt: QrCodeOption) {
		const initend = await this.initDingLoginJs()
		if (!initend) return new Error('初始化DingLoginJs 错误');
		this._dingQrCodeEleId = opt.eleId
		if (!window.DDLogin) {
			return Promise.reject('请加载DDLogin.js文件')
		}
		// dev:'dingoauvewzqtzopzavxsp'
		// test:'dingoarvyggiyjm6nfu6mf'
		// prod:'dingoa1ut0niv1et6ddku3'
		const appId = this.option.SSOServer.includes('auth-dev') ? 'dingoauvewzqtzopzavxsp' : this.option.SSOServer.includes('auth-test') ? 'dingoarvyggiyjm6nfu6mf' : 'dingoa1ut0niv1et6ddku3' // appId 点击创建扫码登录应用授权生成
		window.DDLogin({
			id: opt.eleId, //这里需要你在自己的页面定义一个HTML标签并设置id，例如<div id="login_container"></div>或<span id="login_container"></span>
			goto: encodeURIComponent(
				`https://oapi.dingtalk.com/connect/qrconnect?appid=${appId}&response_type=code&scope=snsapi_login&state=STATE&redirect_uri=${this.option.SSOServer}/web/ding-login`
			), // goto参数,需要进行编码
			style: "border:none;background-color:transparent;", // 二维码的样式
			width: "100%", // 二维码的宽度
			height: "100%" // 二维码的高度
		});
		// **width和height不代表二维码的大小，二维码大小是固定的210px210px。
		this.qrCommonListener(appId,opt.redirectUrl);
	}
	// 赣政通
	qrGzt(opt: QrCodeOption){
		const div = this._$(opt.eleId)
		if(div){
			const style={
				main:' position: relative; height: 260px; width:100%;  overflow: hidden;',
				position:'position: absolute; top:-86px; left:0',
				iframe:'border: none;'
			}
			const serverUrl = `https://login.gzt.drc.jiangxi.gov.cn/oauth2/auth.htm?response_type=code&client_id=4A_dingoa&redirect_uri=${this.option.SSOServer}/gzt-login&scope=get_user_info&authType=QRCODE&embedMode=true`
			div.innerHTML = `<div class="qr-gzt-main" style="${style.main}">
								<div class="qr-gzt-content" style="${style.position}">
									<iframe width="100%" height="330px" src="${serverUrl}" style="${style.iframe}"></iframe>
								</div>
							  </div>`
			this.qrCommonListener('',opt.redirectUrl);
		}
	}
	public qrClean(id:string) {
		if(this._$(id)){
			this._$(id).innerHTML = ""
		}
	}
	// 客户端退出
	public clientLogout() {
		this.removeAllCache()
	}
	/* 退出登录 */
	logout() {
		return new Promise((resolve, reject) => {
			this.serverLogout().then((res) => {
				this.removeAllCache()
				// custom 模式禁止跳转至登录页
				if (this.option.method !== AuthMethod.CUSTOM) {
					const url = encodeURIComponent(this._href)
					if (this.option.DingAutoLogin) {
						window.location.href = `${this.option.SSOServer}/ddLoad?code=${this.option.resourceCode}&returnUrl=${url}`;
					} else {
						window.location.href = `${this.option.SSOServer}?code=${this.option.resourceCode}&returnUrl=${url}`;
					}
				} else {
					return resolve(res)
				}
			}).catch((err) => {
				reject('登出错误' + String(err))
			})
		})
	}
	/**
	 * initDingLoginJs
	 * @return  Promise
	 * @description  初始化异步加载DingLogin Js
	 * @author  jsyang
	 * @date  2022-03-09 14:16:02
	 */
	initDingLoginJs() {
		return new Promise((res, rej) => {
			const scriptSrc = 'https://g.alicdn.com/dingding/dinglogin/0.0.5/ddLogin.js';
			// const scriptSrc = 'https://g.alicdn.com/dingding/h5-dingtalk-login/0.21.0/ddlogin.js';
			const body: HTMLBodyElement = document.querySelector<"body">('body')
			const loaded = !!window.DDLogin;
			if (!loaded) {
				const s = document.createElement('script')
				s.src = scriptSrc;
				s.onload = () => {
					res(true)
				};
				s.onerror = () => {
					rej(false)
				}
				body.appendChild(s)
			} else {
				res(true)
			}
		})
	}
	/* */
	destroy() {
		this.logDestroy && this.logDestroy() // 日志收集销毁
		this._emitter.removeAllListeners();
		this._emitter = null;
	}
}
export { AuthClient }
